package de.gwtprime.server.model;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Query;
import javax.persistence.Table;

import de.gwtprime.server.sql.helper.HibernateHelper;
import de.gwtprime.shared.VoteType;

@Entity
@Table(name = "question")
public class Question extends EntityBaseVoteable implements Comparable<Question>{
	
	/**
	 * SerialVersionUID
	 */
	private static final long serialVersionUID = 1L;

	public Question(){}
	
	private String title;
	
	private String text;
	
	private Date datePosted = new Date();
	
	private ArticleSection articleSection;
	
	private User2 user;
	
	private int answerCount;
	
	private Set<Answer> answers = new HashSet<Answer>();
	
	private Set<VoteQuestion> votes = new HashSet<VoteQuestion>();
	
	private int voteValue;
	
	/**
	 * Is this question marked as completed?
	 */
	private boolean completed;
	
	private String images;

	@Lob
	public synchronized String getText() {
		return text;
	}
	
	public static Question findQuestion(Long id){
		return HibernateHelper.find(Question.class, id);
	}

	public synchronized void setText(String text) {
		this.text = text;
	}

	public synchronized Date getDatePosted() {
		return datePosted;
	}

	public synchronized void setDatePosted(Date datePosted) {
		this.datePosted = datePosted;
	}

	@ManyToOne
	public synchronized ArticleSection getArticleSection() {
		return articleSection;
	}

	public synchronized void setArticleSection(ArticleSection articleSection) {
		this.articleSection = articleSection;
	}

	@OneToMany(mappedBy = "question", cascade = CascadeType.ALL)
	public synchronized Set<Answer> getAnswers() {
		return answers;
	}

	public synchronized void setAnswers(Set<Answer> answers) {
		this.answers = answers;
	}
	
	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn
	public User2 getUser() {
		return user;
	}

	public void setUser(User2 user) {
		this.user = user;
	}

	public Answer addAnswer(Answer answer){

		EntityManager em = HibernateHelper.beginTransactionWithoutTransaction();
		try{
			if(getId()==null) persistPure(em); 

			answer = HibernateHelper.merge(em, answer);
			answer.setQuestion(this);

			if(answers.contains(answer)) return answer;
			answers.add(answer);
			setAnswerCount(getAnswerCount() + 1); 
			persistPure(em);

			return answer;
		}finally{
			em.close();
		}
	}

	public int getAnswerCount() {
		return answerCount;
	}

	public void setAnswerCount(int answerCount) {
		this.answerCount = answerCount;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
	
	public List<VoteAnswer> getAllAnswerVotesForUser(User2 user){
		
		return HibernateHelper.queryDBForList(new VoteAnswer(), 
				String.format("select v from Answer a " +
				"inner join a.votes v where a.question.id=%s " +
				"AND v.user.id='%s'", getId(), user.getId()));
		
	}
	
	public List<VoteAnswerComplete> getAllAnswerVotesCompleteForUser(User2 user){
		
		return HibernateHelper.queryDBForList(new VoteAnswerComplete(), 
				String.format("select v from Answer a " +
				"inner join a.voteAnswerComplete v where a.question.id=%s " +
				"AND v.user.id='%s'", getId(), user.getId()));
	}
	
	
	
	@OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
	public Set<VoteQuestion> getVotes() {
		return votes;
	}

	public void setVotes(Set<VoteQuestion> votes) {
		this.votes = votes;
	}
	
	@Override
	public void decreaseVoteValueAndPersist(){
		decreaseVoteValue();
		persist();
	}
	
	
	
	
	public void addVote(VoteQuestion vote){
		EntityManager em = HibernateHelper.beginTransactionWithoutTransaction();

		try{
			User2 user = vote.getUser();

			if(user==null){
				// Not logged in users can't vote
				return;
			}

			if(getId()==null) persistPure(em);

			VoteQuestion voteFromDB = HibernateHelper.queryDB(new VoteQuestion(), String.format("from %s WHERE question.id=%s AND user.id =%s", VoteQuestion.class.getSimpleName(), getId(), user.getId()));
			// if the user already votes -> just update the existing vote
			// with the new type (UP, DOWN)
			if(voteFromDB!=null){

				// if same type -> no update needed
				if(voteFromDB.getType()==vote.getType()){
					return;
				}

				// if vote is applied in opposite direction -> increase/decrease by 2 
				if(vote.getType()==VoteType.UP){
					voteValue = voteValue + 2;
				}else if(vote.getType()==VoteType.DOWN){
					voteValue = voteValue - 2;
				}
				voteFromDB.setType(vote.getType());
				voteFromDB.mergePure(em);
				return;
			}else{
				vote.setUser((User2)user.mergePure(em));
				vote.setQuestion(this);
				vote.persistPure(em);
			}


			if(votes.contains(vote)) return;
			votes.add(vote);

			if(vote.getType()==VoteType.DOWN)
				voteValue--;
			else if(vote.getType()==VoteType.UP)
				voteValue++;

		}finally{
			em.close();

		}
	}
	
	
	public void addVoteAndPersist(VoteQuestion vote, User2 user){
		vote.setUser(user);
		addVote(vote);
		persist();
	}
	
	public int getVoteValue() {
		return voteValue;
	}

	public void setVoteValue(int voteValue) {
		this.voteValue = voteValue;
	}
	
	
	public VoteQuestion getVoteQuestionForUser(User2 user){
		
		return HibernateHelper.queryDB(new VoteQuestion(), 
				String.format("select v from Question a " +
				"inner join a.votes v where a.id=%s " +
				"AND v.user.id='%s'", getId(), user.getId()));
	}

	@Override
	public int compareTo(Question o) {
		return o.getVoteValue()-getVoteValue();
	}

	public boolean isCompleted() {
		return completed;
	}

	public void setCompleted(boolean completed) {
		
		this.completed = completed;
	}

	@Lob
	public String getImages() {
		return images;
	}

	public void setImages(String images) {
		this.images = images;
	}

	public static List<Question> getQuestionsForUser(String userName, int maxNumberResults){
		
		return HibernateHelper.queryDBForList(new Question(), 
				String.format("from Question as q " +
				"where q.user.email='%s'" , userName), maxNumberResults);
	}
	
	public static List<Question> getLatestQuestions(int numberQuestions) {
		EntityManager entityManager = HibernateHelper.beginTransaction();

		Query query = entityManager.createQuery(String.format(
				"from %s order by id desc", Question.class.getName()));
		query.setMaxResults(numberQuestions);

		@SuppressWarnings("unchecked")
		List<Question> list = (List<Question>) query.getResultList();

		entityManager.close();
		return list;
	}

}
